#!/usr/bin/env node

const fs = require("fs");
const path = require("path");
const { execSync } = require('child_process');

// externals
const semver = tryRequire("semver");

const WORKSPACE = path.dirname(__dirname)
const RAILS_ROOT = path.join(WORKSPACE, "server", "src", "main", "webapp", "WEB-INF", "rails");

const manifest = JSON.parse(fs.readFileSync(path.join(RAILS_ROOT, "package.json")))

function update(dict) {
  interestingKeys(dict).forEach((mod) => {
    const range = dict[mod];
    console.log(`Checking ${mod} -- current: ${range}`);

    // reverse sort to get the latest first
    const versions = JSON.parse(execSync(`npm view ${mod} versions --json`).toString()).reverse();
    const latest = semver.maxSatisfying(versions, range);

    if (range.startsWith("^") || range.startsWith("~")) {
      const parts = range.substring(1).split(".");
      const upgraded = range[0] + latest.split(".").slice(0, parts.length).join(".");
      if (range !== upgraded) {
        dict[mod] = upgraded;
        console.log("  => updating", mod, "to", upgraded);
      }
    }
  });
}

update(manifest.dependencies);
update(manifest.devDependencies);

fs.writeFileSync(path.join(RAILS_ROOT, "package.json"), JSON.stringify(manifest, null, 2) + "\n", {
  encoding: "utf8",
  mode: 0o644
});

// deleting yarn.lock allows us to sort of "tree-shake" our deps by forcing yarn to rebuild
// the depgraph; sometimes newer versions of packages remove dependencies and a normal yarn
// update doesn't seem to prune those old deps from the lockfile.
fs.unlinkSync(path.join(RAILS_ROOT, "yarn.lock"));

console.log("Updating yarn.lock");
execSync("yarn install", { cwd: RAILS_ROOT });

function interestingKeys(dict) {
  return Object.keys(dict).filter((k) => excludes(dict, k));
}

function excludes(dict, key) {
  return !(
    dict[key].startsWith("file:") ||
    dict[key].startsWith("http:") ||
    dict[key].startsWith("https:")
  );
}

function tryRequire(name) {
  try {
    return require(name);
  } catch {
    console.error(`This script requires the package \`${name}\`; please \`cd ${path.relative(process.cwd(), __dirname)} && npm install && cd -\` before running this script`);
    process.exit(1);
  }
}
